/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: mailbox
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2023-06-16     JasonHu           Init
 */

#include <base/mailbox.h>
#include <base/malloc.h>
#include <base/sched.h>

NX_Mailbox *NX_MailboxCreate(NX_Size size)
{
    if (!size)
    {
        return NX_NULL;
    }

    NX_Mailbox *mbox = NX_MemAlloc(sizeof(NX_Mailbox));
    if (mbox == NX_NULL)
    {
        return NX_NULL;
    }

    mbox->fifo = NX_FifoCreate(size * sizeof(NX_UArch));
    if (mbox->fifo == NX_NULL)
    {
        NX_MemFree(mbox);
        return NX_NULL;
    }
    return mbox;
}

NX_Error NX_MailboxDestroy(NX_Mailbox *mbox)
{
    if (mbox == NX_NULL)
    {
        return NX_EINVAL;
    }
    if (mbox->fifo == NX_NULL)
    {
        return NX_EFAULT;
    }
    NX_FifoDestroy(mbox->fifo);
    return NX_EOK;
}

NX_Error NX_MailboxCheckEmpty(NX_Mailbox *mbox)
{
    if (mbox == NX_NULL)
    {
        return NX_EINVAL;
    }
    if (mbox->fifo == NX_NULL)
    {
        return NX_EFAULT;
    }
    
    if (NX_FifoLen(mbox->fifo) != 0)
    {
        return NX_EAGAIN;
    }

    return NX_EOK;
}

static NX_Error DoMailboxTrySend(NX_Mailbox *mbox, NX_UArch data)
{
    NX_UArch tmp;

    /**
     * check box full
     */
    if (NX_FifoAvaliable(mbox->fifo) == 0)
    {
        return NX_ENORES;
    }

    /**
     * put data into fifo
     */
    tmp = data;
    NX_FifoWrite(mbox->fifo, (const NX_U8 *)&tmp, sizeof(NX_UArch));

    return NX_EOK;
}

NX_Error NX_MailboxSend(NX_Mailbox *mbox, NX_UArch data)
{
    if (mbox == NX_NULL)
    {
        return NX_EINVAL;
    }

    if (mbox->fifo == NX_NULL)
    {
        return NX_EFAULT;
    }

    while (DoMailboxTrySend(mbox, data) != NX_EOK)
    {
        NX_SchedYield();
    }
    
    return NX_EOK;
}

NX_Error NX_MailboxSendTimeout(NX_Mailbox *mbox, NX_UArch data, NX_TimeVal ms)
{
    NX_TimeVal tvStart, delta;

    if (mbox == NX_NULL)
    {
        return NX_EINVAL;
    }

    if (mbox->fifo == NX_NULL)
    {
        return NX_EFAULT;
    }

    if (ms == 0)
    {
        return DoMailboxTrySend(mbox, data);
    }

    tvStart = NX_ClockGetMillisecond();
    do
    {
        if (DoMailboxTrySend(mbox, data) == NX_EOK) {
            return NX_EOK;
        }

        delta = NX_ClockGetMillisecond() - tvStart;
        /**
         * TODO: do sched if delta too big.
         */
    } while (delta < ms);

    return NX_ETIMEOUT;
}

static NX_Error DoMailboxTryReceive(NX_Mailbox *mbox, NX_UArch *data)
{
    NX_UArch tmp = 0;

    /**
     * check box full
     */
    if (NX_FifoAvaliable(mbox->fifo) == 0)
    {
        return NX_ENORES;
    }

    /**
     * put data into fifo
     */
    NX_FifoRead(mbox->fifo, (const NX_U8 *)&tmp, sizeof(NX_UArch));

    *data = tmp;

    return NX_EOK;
}

NX_Error NX_MailboxReceiveTimeout(NX_Mailbox *mbox, NX_UArch *data, NX_TimeVal ms)
{
    NX_TimeVal tvStart, delta;

    if (mbox == NX_NULL)
    {
        return NX_EINVAL;
    }

    if (mbox->fifo == NX_NULL)
    {
        return NX_EFAULT;
    }

    if (ms == 0)
    {
        return DoMailboxTryReceive(mbox, data);
    }

    tvStart = NX_ClockGetMillisecond();
    do
    {
        if (DoMailboxTryReceive(mbox, data) == NX_EOK)
        {
            return NX_EOK;
        }

        delta = NX_ClockGetMillisecond() - tvStart;
        /**
         * TODO: do sched if delta too big.
         */
    } while (delta < ms);

    return NX_ETIMEOUT;
}

NX_Error NX_MailboxReceive(NX_Mailbox *mbox, NX_UArch *data)
{
    if (mbox == NX_NULL)
    {
        return NX_EINVAL;
    }

    if (mbox->fifo == NX_NULL)
    {
        return NX_EFAULT;
    }

    while (DoMailboxTryReceive(mbox, data) != NX_EOK)
    {
        NX_SchedYield();
    }

    return NX_EOK;
}
